package openjdk.format;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

public class DateFormatTest {


    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        List<Future<?>> futures = new ArrayList<>();
        for (int i = 0; i < 9; i++) {
            DateFormatTask task = new DateFormatTask();
            Future<?> future = service.submit(task); // submit task to thread pool.
            futures.add(future);
        }

        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (ExecutionException ex) {
                System.err.println("execute error: " + ex.getMessage());
                ex.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        service.shutdown();
    }

    static class DateFormatTask implements Callable<Void> {
        @Override
        public Void call() throws Exception {
            // 使用DateFormatWrapper0 方式错误
            String str = DateFormatWrapper.format(DateFormatWrapper.parse("2017-07-17 16:54:54"));
            System.out.printf("Thread (%s) -> %s\n", Thread.currentThread().getName(), str);
            return null;
        }
    }


    /**
     * 可以发现，SimpleDateFormat 在多线程共享的情况下，不仅可能会出现结果错误的情况，还可能会由于并发访问导致运行异常。
     * 当然，我们肯定有解决的办法：
     * <p>
     * 为 DateFormatWrapper 的 format 和 parse 方法加上 synchronized 关键字，坏处就是前面提到的这会加大线程间的竞争和切换而降低效率；
     * 不使用全局的 SimpleDateFormat 对象，而是每次使用 format 和 parse 方法都新建一个 SimpleDateFormat 对象，
     * 坏处也很明显，每次调用 format 或者 parse 方法都要新建一个 SimpleDateFormat，这会加大 GC 的负担；
     * 使用 ThreadLocal。ThreadLocal<SimpleDateFormat> 可以为每个线程提供一个独立的 SimpleDateFormat 对象，
     * 创建的 SimpleDateFormat 对象个数最多和线程个数相同，相比于 (1)，使用ThreadLocal不存在线程间的竞争；
     * 相比于 (2)，使用ThreadLocal创建的 SimpleDateFormat 对象个数也更加合理（不会超过线程的数量）。
     */

    static class DateFormatWrapper0 {
        private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        public static final String format(Date date) {
            return SDF.format(date);
        }

        public static final Date parse(String string) throws ParseException {
            return SDF.parse(string);
        }
    }


    static class DateFormatWrapper {
        private static final String DATE_FORMAT="yyyy-MM-dd HH:mm:ss";
        private static int count = 0;
        private static final ThreadLocal<SimpleDateFormat> SDF = new ThreadLocal<SimpleDateFormat>() {
            @Override
            protected SimpleDateFormat initialValue() {
                System.out.printf(" >>> count: %d\n",count++ );
                return new SimpleDateFormat(DATE_FORMAT);
            }
        };

        public static final String format(Date dt) {
            return SDF.get().format(dt);
        }

        public static final Date parse(String dt) throws ParseException {
            return SDF.get().parse(dt);
        }
    }
}


